package core;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import java.util.TreeMap;
import java.util.Map;

import core.auxiliar.*;
import core.generator.Skeletons;

/**
 * Singleton para execução de ações semânticas.
 * 
 * @author Rodolpho Uchôa
 * @author Rubens Van Dingenen
 * @author Saulo Toledo
 */
public class SemanticActions {

	/**
	 * Instância única do objeto.
	 */
	private static SemanticActions __instance;
	
	private String moduleName;
	private String lastProcedureName;
	
	// NomeDoProcedimento => LinhaNoCódigoAssemblyGerado
	private Map<String, Integer> procedureLinesAtAssemblyCode;
	
	private List<DeclaredVariable> declaredVariables;
	private List<DeclaredVariable> declaredConstants;
	private List<DeclaredVariable> procedureDeclaredVariables;
	private List<DeclaredProcedure> declaredProcedures;
	
	Stack<String> context;
	

	/**
	 * Construtor da classe.
	 */
	private SemanticActions() {
		this.declaredVariables = new ArrayList<DeclaredVariable>();
		this.declaredConstants = new ArrayList<DeclaredVariable>();
		this.procedureDeclaredVariables = new ArrayList<DeclaredVariable>();
		this.declaredProcedures = new ArrayList<DeclaredProcedure>();
		this.context = new Stack<String>();
		
		// NomeDoProcedimento => LinhaNoCódigoAssemblyGerado
		this.procedureLinesAtAssemblyCode = new TreeMap<String, Integer>();
	}
	
	public void popContext() {
		this.context.pop();
	}



	public Integer procedureLineAtAssemblyCode(String procedureName) {
		return this.procedureLinesAtAssemblyCode.get(procedureName);
	}

	/**
	 * Retorna instância única do objeto.
	 * @return Instância única do objeto.
	 */
	public static SemanticActions getInstance() {

		if (SemanticActions.__instance == null)
			SemanticActions.__instance = new SemanticActions();

		return SemanticActions.__instance;
	}
	
	public void addDeclaredVariable(int line, int column, String varName, SemanticExpressionNode typenode) {
		
		if (this.getDeclaredVariableOrConstantByName(varName) != null)
			throw new SemanticException("Uma variável ou constante com nome '"+ varName +"' já foi declarado neste contexto.",
					line, column);
		
		DeclaredVariable dv = new DeclaredVariable(varName, typenode.getType());
		this.getDeclaredVariables().add(dv);
	}
	
	public void addDeclaredConstant(int line, int column, String varName, SemanticExpressionNode typeAndValueNode) {
		if (this.getDeclaredVariableOrConstantByName(varName) != null)
			throw new SemanticException("Uma variável ou constante com nome '"+ varName +"' já foi declarada neste contexto.",
					line, column);
		
		//TODO: exp é null abaixo para tipos fora do escopo do projeto. Implementá-los futuramente.
		TypeEnum finaltype = TypeEnum.NIL;
		Object finalvalue = null;
		if (typeAndValueNode != null) {
			finaltype = typeAndValueNode.getType();
			finalvalue = typeAndValueNode.getValue();
		}
		
		DeclaredVariable dv = new DeclaredVariable(varName, finaltype);
		dv.setValue(finalvalue);
		this.getDeclaredConstants().add(dv);
	}
	
	// Lembrar de limpar a cada procedimento...
	// Usado para a análise temporária do procedimento...
	public void addProcedureDeclaredVariable(String varName, SemanticExpressionNode typenode) {
		DeclaredVariable dv = new DeclaredVariable(varName, typenode.getType());
		this.getProcedureDeclaredVariables().add(dv);
	}
	
	// A Função é usada apenas para a adição de procedimentos externos no construtor desta classe e faz o mesmo efeito da anterior.
	public void addProcedureDeclaredVariable(String varName, TypeEnum type) {
		DeclaredVariable dv = new DeclaredVariable(varName, type);
		this.getProcedureDeclaredVariables().add(dv);
	}
	
	// Limpa a função anterior...
	public void clearProcedureDeclaredVariables() {
		this.procedureDeclaredVariables = new ArrayList<DeclaredVariable>();
	}
	
	// Método a seguir necessita dos métodos anterioes para funcionar corretamente...
	public void addDeclaredProcedure(String name) {
		DeclaredProcedure dp = new DeclaredProcedure(name);
		Iterator<DeclaredVariable> it = this.getProcedureDeclaredVariables().iterator();
		DeclaredVariable declaredVarTemp;
		while (it.hasNext()) {
			declaredVarTemp = it.next();
			dp.addParam(declaredVarTemp.getName(), declaredVarTemp.getType());
		}
		this.getDeclaredProcedures().add(dp);
		
		this.procedureLinesAtAssemblyCode.put(name, Skeletons.getInstance().getLine());
	}
	
	// Limpa variáveis e procedimentos arquivados para execução em múltiplos arquivos em sequência:
	public void resetAnalyzer() {
		SemanticActions.__instance = new SemanticActions();
		
		Skeletons.getInstance().startSPRegisterStack(1000);
		
		// Adicionando procedimentos predefinidos (já que include de arquivos externos está fora de escopo do projeto):
		this.addProcedureDeclaredVariable("param1", TypeEnum.STRING);
		this.addDeclaredProcedure("INC");
		this.clearProcedureDeclaredVariables();
		Skeletons.getInstance().addFakeProcedure("INC");
		
		this.addProcedureDeclaredVariable("param1", TypeEnum.STRING);
		this.addDeclaredProcedure("DEC");
		this.clearProcedureDeclaredVariables();
		Skeletons.getInstance().addFakeProcedure("DEC");
		
		this.addProcedureDeclaredVariable("param1", TypeEnum.STRING);
		this.addDeclaredProcedure("WriteString");
		this.clearProcedureDeclaredVariables();
		Skeletons.getInstance().addFakeProcedure("WriteString");
		
		this.addProcedureDeclaredVariable("param1", TypeEnum.STRING);
		this.addDeclaredProcedure("WriteInt");
		this.clearProcedureDeclaredVariables();
		Skeletons.getInstance().addFakeProcedure("WriteInt");
		
		this.addProcedureDeclaredVariable("param1", TypeEnum.STRING);
		this.addDeclaredProcedure("WriteReal");
		this.clearProcedureDeclaredVariables();
		Skeletons.getInstance().addFakeProcedure("WriteReal");
		
		this.addProcedureDeclaredVariable("param1", TypeEnum.STRING);
		this.addProcedureDeclaredVariable("param2", TypeEnum.STRING);
		this.addDeclaredProcedure("WriteCard");
		this.clearProcedureDeclaredVariables();
		Skeletons.getInstance().addFakeProcedure("WriteCard");
		
		this.addDeclaredProcedure("WriteLn");
		this.clearProcedureDeclaredVariables();
		Skeletons.getInstance().addFakeProcedure("WriteLn");
	}
	
	// Verifica se um nó de expressão é de determinado tipo.
	public void verifyType(int line, int column, SemanticExpressionNode expressionNodeToVerify, TypeEnum typeToVerify) {
		
		if (expressionNodeToVerify != null)
			if (!expressionNodeToVerify.getType().equals(typeToVerify))
				throw new SemanticException("Tipo de expressão '"+ expressionNodeToVerify.getType() +"' não pode ser usado neste contexto.",
						line, column);
	}
	

	public SemanticExpressionNode createNode(int line, int column,
			TypeEnum typeemum, Object value) {
		
		if (typeemum == null)
			throw new SemanticException("Tipo não pode ser nulo.",
					line, column);
		
		return new SemanticExpressionNode(line, column, typeemum, value);
	}
	
	public SemanticExpressionNode createNodeFromDefinedVariable(Integer line, Integer column, String var) {
		this.checkIfVariableOrConstantIsDefined(line, column, var);
		
		DeclaredVariable dv = this.getDeclaredVariableOrConstantByName(var);

		// dv não pode ser nulo porque houve checagem anterior...
		SemanticExpressionNode nodeAux = new SemanticExpressionNode(line, column, dv.getType(), dv.getValue());
		nodeAux.setName(var);
		return nodeAux;
	}
	
	public SemanticExpressionNode createNodeFromDefinedProcedure(Integer line, Integer column, String procedureName) {
		this.checkIfProcedureIsDefined(line, column, procedureName);
		
		DeclaredProcedure dp = this.getDeclaredProcedureByName(procedureName);
		
		// dp não pode ser nula porque foi feita uma checagem anterior.
		return new SemanticExpressionNode(line, column, dp.getReturnType(), dp.getValue());
	}

	
	public void updateVarValue(String var, Object value) {
		DeclaredVariable dv = this.getDeclaredVariableByName(var);
		dv.setValue(value);
	}
	
	public void checkIfVariableOrConstantIsDefined(Integer line, Integer column, String var) {
		if (this.getDeclaredVariableOrConstantByName(var) == null)
			throw new SemanticException("A variável ou constante '"+ var +"' não foi declarada neste contexto.",
					line, column);
	}
	
	public void checkIfProcedureIsDefined(Integer line, Integer column, String procedureName) {
		if (this.getDeclaredProcedureByName(procedureName) == null)
			throw new SemanticException("O procedimento '"+ procedureName +"' não foi definido.",
					line, column);
	}
	
	// Só para variáveis
	public void checkAssignmentType(Integer line, Integer column, String var, SemanticExpressionNode expression) {
		// Checagem comentada porque é feita no .cup. Fica mais explícita a checagem ao ler o .cup. 
		//this.checkIfVariableIsDefined(line, column, var);
		
		DeclaredVariable dv = this.getDeclaredVariableByName(var);
		// dv não pode ser nula porque foi feita uma checagem anterior (a comentada).

		// TODO: mudar isso ao implementar tipos em arrays:
		if (!dv.getType().equals(TypeEnum.ARRAY))
			if (!dv.getType().equals(expression.getType()))
				throw new SemanticException("Tipos incompatíveis em atribuição: '"+ dv.getType() +"' e '"+ expression.getType() +"'.",
						line, column);
		
		
	}
	

	public void setDeclaredProcedureReturnType(String procname, TypeEnum returnType) {
		DeclaredProcedure dp = this.getDeclaredProcedureByName(procname);
		dp.setReturnType(returnType);
	}
	
	private DeclaredVariable getDeclaredVariableByName(String varName) {
		// Procura nas variáveis globais:
		Iterator<DeclaredVariable> it = this.getDeclaredVariables().iterator();
		DeclaredVariable aux = null;
		while (it.hasNext()) {
			aux = it.next();
			if (aux.getName().equals(varName))
				return aux;
		}

		// E agora nas variáveis de procedimento:
		it = this.getProcedureDeclaredVariables().iterator();
		aux = null;
		while (it.hasNext()) {
			aux = it.next();
			if (aux.getName().equals(varName))
				return aux;
		}
		
		return null;
	}
	
	private DeclaredVariable getDeclaredVariableOrConstantByName(String varName) {
		
		// Checa variáveis globais e de procedimento:
		DeclaredVariable aux = this.getDeclaredVariableByName(varName);
		if (aux != null)
			return aux;
		
		// Checa as constantes definidas:
		Iterator<DeclaredVariable> it = this.getDeclaredConstants().iterator();
		aux = null;
		while (it.hasNext()) {
			aux = it.next();
			if (aux.getName().equals(varName))
				return aux;
		}
		
		return null;
	}
	
	private DeclaredProcedure getDeclaredProcedureByName(String procedureName) {
		// Checa as constantes definidas:
		Iterator<DeclaredProcedure> it = this.getDeclaredProcedures().iterator();
		DeclaredProcedure aux = null;
		while (it.hasNext()) {
			aux = it.next();
			if (aux.getName().equals(procedureName))
				return aux;
		}
		
		return null;
	}

	public SemanticExpressionOperatorNode createOperationNode(Integer line,
			Integer column, String operator, SemanticExpressionNode node) {
		return new SemanticExpressionOperatorNode(line, column, operator, node);
	}
	
	
	
	public String getModuleName() {
		return this.moduleName;
	}

	public void setModuleName(String moduleName) {
		this.moduleName = moduleName;
	}
	
	public void verifyModuleName(Integer line,
			Integer column, String moduleName) {
		
		if (!this.getModuleName().equals(moduleName))
			throw new SemanticException("Fim de módulo '" + moduleName + "' encontrado, "
					+ "mas deveria ser '"+ this.getModuleName() +"'.",
					line, column);
	}
	
	public void setLastProcedureName(String lastProcedureName) {
		this.lastProcedureName = lastProcedureName;
	}
	
	public String getLastProcedureName() {
		return this.lastProcedureName;
	}
	
	public void verifyProcedureName(Integer line,
			Integer column, String procedureName) {
		
		if (this.getDeclaredProcedureByName(procedureName) == null)
			throw new SemanticException("Fim de procedimento '" + procedureName + "' encontrado, "
					+ "mas deveria ser '"+ this.getLastProcedureName() +"'.",
					line, column);
	}

	public SemanticExpressionNode realizeUnaryOperation(Integer line,
			Integer column, String operator, SemanticExpressionNode node) {
		
		Boolean finalValue = false;
		
		// Em nossa gramática o único operador unário é o de negação, mas
		// há espaço aqui para outros casos
		if (operator.equals(SemanticConstants.OP_UNARY_NOT)) {
		
			if (node.getType().equals(TypeEnum.BOOLEAN)) {
				if ((Boolean) node.getValue() == true)
					finalValue = false;
				else
					finalValue = true;
			} else if(node.getType().equals(TypeEnum.INTEGER)) {
				if ((Integer) node.getValue() == 1)
					finalValue = false;
				else
					finalValue = true;
					
			} else if(node.getType().equals(TypeEnum.LONGINT)) {
				if ((Long) node.getValue() == 1)
					finalValue = false;
				else
					finalValue = true;
					
			} else if(node.getType().equals(TypeEnum.REAL)) {
				if ((Float) node.getValue() == Float.valueOf(1))
					finalValue = false;
				else
					finalValue = true;
					
			} else if(node.getType().equals(TypeEnum.LONGREAL)) {
				if ((Double) node.getValue() == Double.valueOf(1))
					finalValue = false;
				else
					finalValue = true;
					
			} else {
				throw new SemanticException(operator
						+ " não é um operador valido para os operandos.",
						line, column);
			}
		} else {
			throw new SemanticException(operator
					+ " não é um operador valido para os operandos.",
					line, column);
		}
		
		return new SemanticExpressionNode(line, column, TypeEnum.BOOLEAN, finalValue);
	}
	
	public SemanticExpressionNode realizeExpressionRelation(Integer line,
			Integer column, String relation, SemanticExpressionNode node1,
			SemanticExpressionNode node2) {

		Boolean finalValue = false;
		
		
		if (node1.getType().equals(TypeEnum.INTEGER)) {
			if (node2.getType().equals(TypeEnum.INTEGER)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Integer) node1.getValue()).equals((Integer) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Integer) node1.getValue()).compareTo((Integer) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Integer) node1.getValue()).compareTo((Integer) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Integer) node1.getValue()).compareTo((Integer) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Integer) node1.getValue()).compareTo((Integer) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Integer) node1.getValue()).equals((Integer) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
			} else if (node2.getType().equals(TypeEnum.LONGINT)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Long) node1.getValue()).equals((Long) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Long) node1.getValue()).compareTo((Long) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Long) node1.getValue()).compareTo((Long) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Long) node1.getValue()).compareTo((Long) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Long) node1.getValue()).compareTo((Long) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Long) node1.getValue()).equals((Long) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
			} else if (node2.getType().equals(TypeEnum.REAL)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Float) node1.getValue()).equals((Float) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Float) node1.getValue()).compareTo((Float) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Float) node1.getValue()).compareTo((Float) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Float) node1.getValue()).compareTo((Float) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Float) node1.getValue()).compareTo((Float) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Float) node1.getValue()).equals((Float) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
			} else if (node2.getType().equals(TypeEnum.LONGREAL)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
				
			} else if (node2.getType().equals(TypeEnum.ARRAY)) {
				//TODO: Tipos em arrays não estão implementados ainda, fazê-lo...
			} else {
				throw new SemanticException(node2.getType()
						+ " não é um operando valido para o operador.", line,
						column);
			}
		} else if (node1.getType().equals(TypeEnum.LONGINT)) {
			if (node2.getType().equals(TypeEnum.INTEGER)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Long) node1.getValue()).equals((Long) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Long) node1.getValue()).compareTo((Long) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Long) node1.getValue()).compareTo((Long) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Long) node1.getValue()).compareTo((Long) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Long) node1.getValue()).compareTo((Long) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Long) node1.getValue()).equals((Long) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
			} else if (node2.getType().equals(TypeEnum.LONGINT)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Long) node1.getValue()).equals((Long) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Long) node1.getValue()).compareTo((Long) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Long) node1.getValue()).compareTo((Long) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Long) node1.getValue()).compareTo((Long) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Long) node1.getValue()).compareTo((Long) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Long) node1.getValue()).equals((Long) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
			} else if (node2.getType().equals(TypeEnum.REAL)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
			} else if (node2.getType().equals(TypeEnum.LONGREAL)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
				
			} else if (node2.getType().equals(TypeEnum.ARRAY)) {
				//TODO: Tipos em arrays não estão implementados ainda, fazê-lo...
			} else {
				throw new SemanticException(node2.getType()
						+ " não é um operando valido para o operador.", line,
						column);
			}

		} else if (node1.getType().equals(TypeEnum.REAL)) {
			if (node2.getType().equals(TypeEnum.INTEGER)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Float) node1.getValue()).equals((Float) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Float) node1.getValue()).compareTo((Float) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Float) node1.getValue()).compareTo((Float) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Float) node1.getValue()).compareTo((Float) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Float) node1.getValue()).compareTo((Float) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Float) node1.getValue()).equals((Float) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
			} else if (node2.getType().equals(TypeEnum.LONGINT)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
			} else if (node2.getType().equals(TypeEnum.REAL)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
			} else if (node2.getType().equals(TypeEnum.LONGREAL)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
				
			} else if (node2.getType().equals(TypeEnum.ARRAY)) {
				//TODO: Tipos em arrays não estão implementados ainda, fazê-lo...
			} else {
				throw new SemanticException(node2.getType()
						+ " não é um operando valido para o operador.", line,
						column);
			}

		} else if (node1.getType().equals(TypeEnum.LONGREAL)) {
			if (node2.getType().equals(TypeEnum.INTEGER)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
			} else if (node2.getType().equals(TypeEnum.LONGINT)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
			} else if (node2.getType().equals(TypeEnum.REAL)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
			} else if (node2.getType().equals(TypeEnum.LONGREAL)) {
				if (relation.equals(SemanticConstants.REL_EQ))
					if (((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) <= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_LT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) < 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GE))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) >= 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_GT))
					if (((Double) node1.getValue()).compareTo((Double) node2.getValue()) > 0)
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_NE))
					if (!((Double) node1.getValue()).equals((Double) node2.getValue()))
						finalValue = true;
					else
						finalValue = false;
				
				else if (relation.equals(SemanticConstants.REL_IN))
					//TODO: Implementar...
					throw new SemanticException("Operador não implementado na análise semântica por estar fora do escopo do projeto.", line, column);
				
				else {
					throw new SemanticException(relation
							+ " não é uma relação válida para operandos '"
							+ node1.getType().getName() + "' e '"
							+ node2.getType().getName() + "'", line, column);
				}
				
			} else if (node2.getType().equals(TypeEnum.ARRAY)) {
				//TODO: Tipos em arrays não estão implementados ainda, fazê-lo...
			} else {
				throw new SemanticException(node2.getType()
						+ " não é um operando valido para o operador.", line,
						column);
			}

		} else {
			throw new SemanticException(node1.getType()
					+ " não é um operando valido para o operador.", line,
					column);
		}
		
		return new SemanticExpressionNode(line, column, TypeEnum.BOOLEAN, finalValue);
	}
	
	public SemanticExpressionOperatorNode realizePartialOperation(Integer line,
			Integer column, String operator, SemanticExpressionNode normalNode,
			SemanticExpressionOperatorNode partialNode) {
		
		/*
		if (normalNode.getType() == null)
			throw new SemanticException("DEBUG", line, column);
		/**/
		
		SemanticExpressionNode nodeToOperation = null;
		String operatorToOperation = null;
		if (partialNode != null) {
			operatorToOperation = partialNode.getOperator();
			nodeToOperation = partialNode.getNode();
		}

		
		
		SemanticExpressionNode node = this.realizeOperation(line, column,
				operatorToOperation, normalNode, nodeToOperation);
		
		return new SemanticExpressionOperatorNode(line, column, operator, node);
	}
	
	
	public SemanticExpressionNode realizeConstantOperation(int line, int column, String signal,
			SemanticExpressionNode node1, SemanticExpressionOperatorNode node2) {
		
		if (signal != null && signal.equals(SemanticConstants.OP_MINUS)) {
			if (node1.getType().equals(TypeEnum.INTEGER)){
				node1.setValue((Integer) node1.getValue() * -1);
			} else if (node1.getType().equals(TypeEnum.LONGINT)){
				node1.setValue((Long) node1.getValue() * -1);
			} else if (node1.getType().equals(TypeEnum.REAL)){
				node1.setValue((Float) node1.getValue() * -1);
			} else if (node1.getType().equals(TypeEnum.LONGREAL)){
				node1.setValue((Double) node1.getValue() * -1);
			}  else {
				throw new SemanticException(signal
						+ " não compativel com o operando.",
						line, column);
			}
		}
		
		
		String operator = null;
		SemanticExpressionNode node2ToOperation = null;
		if (node2 != null) {
			operator = node2.getOperator();
			node2ToOperation = node2.getNode();
		}
		
		return this.realizeOperation(line, column, operator, node1, node2ToOperation);
	}

	public SemanticExpressionNode realizeOperation(int line, int column,
			String operator, SemanticExpressionNode node1,
			SemanticExpressionNode node2) {

		//if (node1 == null)
		//	return null;
		
		if (node2 == null) {
			return node1;
		}
		
		Object node1Value = node1.getValue();
		Object node2Value = node2.getValue();
		if (node1Value == null)
			node1Value = 0;
		if (node2Value == null)
			node2Value = 0;
		
		TypeEnum finalType = null;
		Object finalValue = null;

		if (node1.getType().equals(TypeEnum.INTEGER)) {
			if (node2.getType().equals(TypeEnum.INTEGER)) {
				finalType = TypeEnum.INTEGER;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Integer) node1Value
							+ (Integer) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Integer) node1Value
							- (Integer) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Integer) node1Value
							* (Integer) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Integer) node1Value
							/ (Integer) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para "
							+ TypeEnum.INTEGER.getName(), line, column);
				}
			} else if (node2.getType().equals(TypeEnum.LONGINT)) {
				finalType = TypeEnum.LONGINT;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Integer) node1Value
							+ (Long) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Integer) node1Value
							- (Long) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Integer) node1Value
							* (Long) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Integer) node1Value
							/ (Long) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para os operandos.",
							line, column);
				}
			} else if (node2.getType().equals(TypeEnum.REAL)) {
				finalType = TypeEnum.REAL;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Integer) node1Value
							+ (Float) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Integer) node1Value
							- (Float) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Integer) node1Value
							* (Float) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Integer) node1Value
							/ (Float) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para os operandos.",
							line, column);
				}
			} else if (node2.getType().equals(TypeEnum.LONGREAL)) {
				finalType = TypeEnum.LONGREAL;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Integer) node1Value
							+ (Double) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Integer) node1Value
							- (Double) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Integer) node1Value
							* (Double) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Integer) node1Value
							/ (Double) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para os operandos.",
							line, column);
				}
				
			} else if (node2.getType().equals(TypeEnum.ARRAY)) {
				finalType = TypeEnum.ARRAY;
				//TODO: Tipos em arrays não estão implementados ainda, fazê-lo...
			} else {
				throw new SemanticException(node2.getType()
						+ " não é um operando valido para o operador.", line,
						column);
			}
		} else if (node1.getType().equals(TypeEnum.LONGINT)) {
			if (node2.getType().equals(TypeEnum.LONGINT)) {
				finalType = TypeEnum.LONGINT;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Long) node1Value
							+ (Long) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Long) node1Value
							- (Long) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Long) node1Value
							* (Long) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Long) node1Value
							/ (Long) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para "
							+ TypeEnum.LONGINT.getName(), line, column);
				}
			} else if (node2.getType().equals(TypeEnum.INTEGER)) {
				finalType = TypeEnum.LONGINT;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Long) node1Value
							+ (Integer) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Long) node1Value
							- (Integer) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Long) node1Value
							* (Integer) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Long) node1Value
							/ (Integer) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para os operandos.",
							line, column);
				}
			} else if (node2.getType().equals(TypeEnum.REAL)) {
				finalType = TypeEnum.REAL;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Long) node1Value
							+ (Float) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Long) node1Value
							- (Float) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Long) node1Value
							* (Float) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Long) node1Value
							/ (Float) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para os operandos.",
							line, column);
				}
			} else if (node2.getType().equals(TypeEnum.LONGREAL)) {
				finalType = TypeEnum.LONGREAL;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Long) node1Value
							+ (Double) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Long) node1Value
							- (Double) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Long) node1Value
							* (Double) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Long) node1Value
							/ (Double) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para os operandos.",
							line, column);
				}
				
			} else if (node2.getType().equals(TypeEnum.ARRAY)) {
				finalType = TypeEnum.ARRAY;
				//TODO: Tipos em arrays não estão implementados ainda, fazê-lo...
			} else {
				throw new SemanticException(node2.getType()
						+ " não é um operando valido para o operador.", line,
						column);
			}

		} else if (node1.getType().equals(TypeEnum.REAL)) {
			if (node2.getType().equals(TypeEnum.REAL)) {
				finalType = TypeEnum.REAL;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Float) node1Value
							+ (Float) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Float) node1Value
							- (Float) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Float) node1Value
							* (Float) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Float) node1Value
							/ (Float) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para "
							+ TypeEnum.REAL.getName(), line, column);
				}
			} else if (node2.getType().equals(TypeEnum.INTEGER)) {
				finalType = TypeEnum.REAL;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Float) node1Value
							+ (Integer) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Float) node1Value
							- (Integer) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Float) node1Value
							* (Integer) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Float) node1Value
							/ (Integer) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para os operandos.",
							line, column);
				}
			} else if (node2.getType().equals(TypeEnum.LONGINT)) {
				finalType = TypeEnum.REAL;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Float) node1Value
							+ (Long) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Float) node1Value
							- (Long) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Float) node1Value
							* (Long) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Float) node1Value
							/ (Long) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para os operandos.",
							line, column);
				}
			} else if (node2.getType().equals(TypeEnum.LONGREAL)) {
				finalType = TypeEnum.LONGREAL;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Float) node1Value
							+ (Double) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Float) node1Value
							- (Double) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Float) node1Value
							* (Double) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Float) node1Value
							/ (Double) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para os operandos.",
							line, column);
				}
				
			} else if (node2.getType().equals(TypeEnum.ARRAY)) {
				finalType = TypeEnum.ARRAY;
				//TODO: Tipos em arrays não estão implementados ainda, fazê-lo...
			} else {
				throw new SemanticException(node2.getType()
						+ " não é um operando valido para o operador.", line,
						column);
			}

		} else if (node1.getType().equals(TypeEnum.LONGREAL)) {
			if (node2.getType().equals(TypeEnum.LONGREAL)) {
				finalType = TypeEnum.LONGREAL;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Double) node1Value
							+ (Double) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Double) node1Value
							- (Double) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Double) node1Value
							* (Double) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Double) node1Value
							/ (Double) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para "
							+ TypeEnum.LONGREAL.getName(), line, column);
				}
			} else if (node2.getType().equals(TypeEnum.INTEGER)) {
				finalType = TypeEnum.LONGREAL;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Double) node1Value
							+ (Integer) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Double) node1Value
							- (Integer) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Double) node1Value
							* (Integer) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Double) node1Value
							/ (Integer) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para os operandos.",
							line, column);
				}
			} else if (node2.getType().equals(TypeEnum.LONGINT)) {
				finalType = TypeEnum.LONGREAL;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Double) node1Value
							+ (Long) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Double) node1Value
							- (Long) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Double) node1Value
							* (Long) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Double) node1Value
							/ (Long) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para os operandos.",
							line, column);
				}
			} else if (node2.getType().equals(TypeEnum.REAL)) {
				finalType = TypeEnum.LONGREAL;
				if (operator.equals(SemanticConstants.OP_PLUS))
					finalValue = (Double) node1Value
							+ (Float) node2Value;
				else if (operator.equals(SemanticConstants.OP_MINUS)) {
					finalValue = (Double) node1Value
							- (Float) node2Value;
				} else if (operator.equals(SemanticConstants.OP_TIMES)) {
					finalValue = (Double) node1Value
							* (Float) node2Value;
				} else if (operator.equals(SemanticConstants.OP_DIV)
						|| operator.equals(SemanticConstants.OP_DIVISION)) {
					finalValue = (Double) node1Value
							/ (Float) node2Value;
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para os operandos.",
							line, column);
				}
				
			} else if (node2.getType().equals(TypeEnum.ARRAY)) {
				finalType = TypeEnum.ARRAY;
				//TODO: Tipos em arrays não estão implementados ainda, fazê-lo...
			} else {
				throw new SemanticException(node2.getType()
						+ " não é um operando valido para o operador.", line,
						column);
			}

		} else if (node1.getType().equals(TypeEnum.BOOLEAN)) {
			if (node2.getType().equals(TypeEnum.BOOLEAN)) {
				finalType = TypeEnum.BOOLEAN;
				if (operator.equals(SemanticConstants.OP_LOGICAL_AND)) {
					finalValue = (Boolean) node1.getValue() && (Boolean) node2.getValue();
				} else if (operator.equals(SemanticConstants.OP_LOGICAL_OR)) {
					finalValue = (Boolean) node1.getValue() || (Boolean) node2.getValue();
				} else {
					throw new SemanticException(operator
							+ " não é um operador valido para os operandos.",
							line, column);
				}
			} else {
				throw new SemanticException(node2.getType()
						+ " não é um operando valido para o operador.", line,
						column);
			}
		} else {
			throw new SemanticException(node1.getType()
					+ " não é um operando valido para o operador.", line,
					column);
		}
		
		return new SemanticExpressionNode(line, column, finalType, finalValue);
	}
	
	

	
	public List<DeclaredVariable> getDeclaredVariables() {
		return this.declaredVariables;
	}

	public List<DeclaredVariable> getDeclaredConstants() {
		return this.declaredConstants;
	}

	public List<DeclaredVariable> getProcedureDeclaredVariables() {
		return this.procedureDeclaredVariables;
	}

	public List<DeclaredProcedure> getDeclaredProcedures() {
		return this.declaredProcedures;
	}
}
